library ieee;					-- packages:
use ieee.std_logic_1164.all;			--   std_logic
use ieee.numeric_std.all;			--   (un)signed
use work.all;
use constants.all;

-- TODO imemAddr/dmemAddrWrite/dmemAddrRead possible hazards if read/write 

entity cpu_mem_wrapper is
	generic(sramAddrWd : natural range 2 to 32 := 32;
			sramDataWd : natural range 2 to 32 := 32);	
	port(clock, reset: in std_logic;
		sram_nWE, sram_nOE: out std_logic;
		sramNByteMask: out std_logic_vector(3 downto 0);
		sramAddr : out std_logic_vector(sramAddrWd - 1 downto 0);
		sramData : inout std_logic_vector(sramDataWd - 1 downto 0));
end entity;

architecture cpu_mem_wrapper_arch of cpu_mem_wrapper is
	-- cpu inserts a pipeline bubble if cpuWaitCycle = '1'
	signal cpuWaitCycle: std_logic;

	-- CPU instruction memory/cache interface
    signal imemAddr, imemData :  b32;

	-- CPU data memory/cache interface
   signal dmemAddrRead, dmemAddrWrite, dmemDataRead, dmemDataWrite : b32;
	signal dmemRdEna, dmemWrEna : std_logic;
	signal dmemNByteMask : std_logic_vector(3 downto 0);
	
	type MemStateType is (READ_INSTRUCTION, WRITE_DATA, READ_DATA);
	signal state : MemStateType;	
begin
	CPU: entity work.cpu port map(clock => clock, reset => reset, imemAddr => imemAddr,
        		imemData => imemData, dmemAddrRead => dmemAddrRead, dmemAddrWrite => dmemAddrWrite,
				dmemWrEna => dmemWrEna, dmemRdEna => dmemRdEna, dmemNByteMask => dmemNByteMask,
				dmemDataRead => dmemDataRead, dmemDataWrite => dmemDataWrite, doWait => cpuWaitCycle);		
		
	sramData <= (others => 'Z');	
	
	process(clock, reset)
		procedure sramSetWriteMode is
		begin	
			sram_nWE <= '0'; -- enable sram write
			sram_nOE <= '1'; -- disable sram output	
		end	procedure; 
		procedure sramSetReadMode is
		begin
			sram_nWE <= '1'; -- disable sram write
			sram_nOE <= '0'; -- enable sram output			
		end	procedure; 
			
	begin										
		
		if rising_edge(clock) then
			if reset = '1' then
				sram_nWE <= '1';
				sram_nOE <= '1';
				sramAddr <= (others => '-');
				sramData <= (others => '-');
				sramNByteMask <= (others => '-');
				cpuWaitCycle <= '1';
				state <= READ_INSTRUCTION;				
				
			else		
				assert not (dmemRdEna = '1' and dmemWrEna = '1') severity error; -- man kann lesen (exklusiv-)oder schreiben
				
				if state = READ_INSTRUCTION then																									
					dmemDataRead <= (others => '-');
					imemData <= sramData; -- forward lates instruction to cpu
					
					-- no extra waits necessary, read next instruction
					if dmemRdEna = '0' and dmemWrEna = '0' then
						sramAddr <= imemAddr;
						sramNByteMask <= "0000"; -- read complete istruction
						sramSetReadMode;	
						state <= READ_INSTRUCTION;										
						cpuWaitCycle <= '0';
						
					-- data read request, have to wait a cycle fo the fetch
					elsif dmemRdEna = '1' then 
						sramAddr <= dmemAddrRead;
						sramNByteMask <= dmemNByteMask;
						sramSetReadMode;
						state <= READ_DATA;
						cpuWaitCycle <= '1';
					
					-- data write request, have to wait a cycle fo the it to complete						
					elsif dmemWrEna = '1' then
					   sramAddr <= dmemAddrWrite;
						sramNByteMask <= dmemNByteMask;
						sramData <= dmemDataWrite;
						sramSetWriteMode;
						state <= WRITE_DATA;
						cpuWaitCycle <= '1';						
							
					end if;										
					
				else
					if state = READ_DATA then
						dmemDataRead <= sramData; -- read out the data
					else -- WRITE_DATA, just wait
						dmemDataRead <= (others => '-');
					end if;
					
					sramAddr <= imemAddr; -- prepare the next instruction
					sramNByteMask <= "0000";
					sramSetReadMode;															
					state <= READ_INSTRUCTION;
					cpuWaitCycle <= '0';
															
				end if;				
			end if;									
		end if;
	end process;
								
end architecture cpu_mem_wrapper_arch;